//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Media;

namespace OpenLS.Chart.UI
{
    class CurveFactory
    {
        public CurveFactory(PointCollection points)
        {
            this.points = points;
        }

        private readonly PointCollection points;
        public static Geometry CreateCurve (PointCollection spoints, bool isSmooth)
        {
            if (spoints.Count == 0)
                return null;
            if (!isSmooth)
                return new PathGeometry(new List<PathFigure>
                                            {
                                                new PathFigure(spoints[0], new List<PathSegment>
                                                                               {
                                                                                   new PolyLineSegment(spoints, true)
                                                                               }, false)
                                            });

            IEnumerable<PathSegment> segments = new CurveFactory(spoints).GetSegments();
            var gg = new PathGeometry(new List<PathFigure>
            {
                new PathFigure(spoints[0], segments ,false)
            });
            return gg;

        }

        private IEnumerable<PathSegment> GetSegments()
        {
            Contract.Assert(points.Count > 1);
            if (points.Count == 2)
                yield return GetSegment();
            if (points.Count == 3)
            {
                yield return GetEndBezierSegment(points[0], points[1], points[2], true);
                yield return GetEndBezierSegment(points[0], points[1], points[2], false);
            }
            else
            {
                yield return GetEndBezierSegment(points[0], points[1], points[2], true);
                yield return GetPolyBezierSegment();
                yield return GetEndBezierSegment(points[points.Count - 3],
                                           points[points.Count - 2],
                                           points[points.Count - 1], false);


            }
            
        }

        private double k = 1;
        PolyBezierSegment GetPolyBezierSegment()
        {
            Contract.Requires(points.Count >= 3);
            {
                var segment = new PolyBezierSegment();
                for (int i = 0; i <= points.Count - 2; i++)
                {
                    if (i == 0)
                        continue;
                    if (i == points.Count - 2)
                        continue;
                    Point a, d;
                    Point b = points[i];
                    Point c = points[i + 1];
                    if (i == 0)
                    {
                        a = b + (b - c);
                    }
                    else
                    {
                        a = points[i - 1];
                    }
                    if (i == points.Count - 2)
                    {
                        d = c + (c - b);
                    }
                    else
                        d = points[i + 2];
                    Point p1, p2, unused;
                    GetControlPoints(a, b, c, out unused, out p1);
                    GetControlPoints(b, c, d, out p2, out unused);
                    segment.Points.Add(p1);
                    segment.Points.Add(p2);
                    segment.Points.Add(c);
                }
                return segment;
            }
        }

        static QuadraticBezierSegment GetEndBezierSegment(Point a, Point b, Point c, bool first)
        {
            Point before;
            Point after;
            GetControlPoints(a, b, c, out before, out after);
            if (first)
                return new QuadraticBezierSegment (before, b, true);
            return new QuadraticBezierSegment(after, c, true);
        }

        static void GetControlPoints(Point a, Point b, Point c, out Point before, out Point after)
        {
            Vector ab = b - a;
            Vector bc = c - b;
            Point mid1 = a + (ab) / 2;
            Point mid2 = b + (bc) / 2;
            double ratio = ab.Length / (ab.Length + bc.Length);
            Point mid = mid1 + (mid2 - mid1) * ratio;
            before = mid1 + (b - mid);
            after = mid2 + (b - mid);

        }


        internal PathSegment GetSegment()
        {
            if (points.Count <= 2)
                return new PolyLineSegment(points, true)
                    ;
            return GetPolyBezierSegment();
        }
    }
}
